import { DataSource } from '@angular/cdk/table';
import { BehaviorSubject, combineLatest, merge, of, Subscription, Subject, Observable } from 'rxjs';
import { map, startWith, debounceTime, skip, delay } from 'rxjs/operators';
import { orderBy as _orderBy, forEach as _forEach } from 'lodash';
import { SortDirective, SortConfig } from '../sort/api';
import { PaginatorComponent, PageConfig } from '../pagination/api';
import { FilterUtils, TreeFilterUtils } from '../core/api';
import { TableFilterConfig, TableFilterConfig as FilterConfig } from './interface';

export class TableDataSource<T> extends DataSource<T> {
  // 是否为异步，异步时认为data永远是经过条件处理的
  _async: boolean;

  // 是否遍历, 遍历要处理子
  _traversal: boolean;

  _listSubscription = new Map<string, any>();

  _stateChangesSubscription = Subscription.EMPTY;

  // renderdata变更流的订阅
  _renderChangesSubscription = Subscription.EMPTY;
  // 组件
  private _sort: SortDirective;

  private _paginator: PaginatorComponent;
  // 源数据
  private readonly _data$: BehaviorSubject<T[]>;
  // renderdata流，依赖data变更
  private readonly _renderData$ = new Subject<T[]>();

  // filter导致page变更的记录流
  private _internalPageChange$ = new Subject<void>();

  // table的所有state变更流
  private readonly _state$ = new Subject<{ [key: string]: any }>();
  private readonly filteredData$ = new Subject<{ [key: string]: any }>();

  private _pageState$: BehaviorSubject<PageConfig> = new BehaviorSubject(null);

  // 内置的sort filter page三种条件管理流
  private _sortState$: BehaviorSubject<Map<string, SortConfig>> = new BehaviorSubject(null);

  private _filterState$: BehaviorSubject<Map<string, FilterConfig>> = new BehaviorSubject(null);

  /**
   * x 和 xState 区别
   * x：代表[lvX]='x'组件绑定方式设置，xchange通过绑定自动管理
   * xState：代表x组件的xchange事件监听条件变化，手动注册到table条件里
   */
  constructor(initialData: T[] = [], async: boolean = false, traversal: boolean = false) {
    super();
    this._data$ = new BehaviorSubject<T[]>(initialData);
    this._async = async;
    this._traversal = traversal;
    this._updateChangeSubscription();
  }

  get async(): boolean {
    return this._async;
  }

  // 源数据
  get data(): T[] {
    return this._data$.value;
  }

  // sort direcitve NEED_DO：sort sortstate冲突
  get sort(): SortDirective {
    return this._sort;
  }

  get traversal(): boolean {
    return this._traversal;
  }

  // page direcitve
  get paginator(): PaginatorComponent {
    return this._paginator;
  }

  set data(data: T[]) {
    this._data$.next(data);
  }

  set async(value: boolean) {
    this._async = value;
    this._updateChangeSubscription();
  }

  set sort(data: SortDirective) {
    this._sort = data;
    this._updateChangeSubscription();
  }

  set traversal(status: boolean) {
    this._traversal = status;
    this._updateChangeSubscription();
  }

  set paginator(data: PaginatorComponent) {
    this._paginator = data;
    this._updateChangeSubscription();
  }

  disconnect(): void {}

  states(): Subject<any> {
    return this._state$;
  }

  connect(): Subject<any> {
    return this._renderData$;
  }

  filters(): Subject<any> {
    return this.filteredData$;
  }

  getPaginatorState(): BehaviorSubject<PageConfig> {
    return this._pageState$;
  }

  // page state
  setPaginatorState(data: PageConfig): void {
    return this._pageState$.next(data);
  }

  getSortState(): BehaviorSubject<Map<string, SortConfig>> {
    return this._sortState$;
  }

  // fitler state
  setFilterState(data: Map<string, FilterConfig>): void {
    this._filterState$.next(data);
  }

  // sort state
  setSortState(data: Map<string, SortConfig>): void {
    return this._sortState$.next(data);
  }

  getFilterState(): BehaviorSubject<Map<string, TableFilterConfig>> {
    return this._filterState$;
  }

  // NEED_DO: 处理优化, 逻辑不清晰 冗余
  /**
   * 条件管理
   */
  _updateAsyncChangeSubscription(): void {
    this._updateStateSubscription();
    this._updateDataSubscription();
  }

  _isInvalid(val): boolean {
    return (typeof val === 'number' && isNaN(val)) || [null, undefined, ''].includes(val);
  }

  /**
   *
   * @param pure 是否有初始值
   */
  getSort(pure: boolean = false): any {
    const { sort } = this;
    if (!sort) {
      return of(null);
    }
    return !pure ? sort.lvSortChange.pipe(startWith({})) : sort.lvSortChange;
  }

  _sortStateData(data: T[], sortMap: Map<string, SortConfig>): T[] {
    let _res = data;
    if (sortMap && data.length) {
      sortMap.forEach((item): T[] => (_res = this._sortData(_res, item)));
    }
    return _res;
  }

  _updateDataSubscription(): void {
    this._renderChangesSubscription.unsubscribe();
    const dataStream = this._data$;
    this._renderChangesSubscription = dataStream.subscribe((res): void => {
      this._renderData$.next(res);
    });
  }

  // 遍历场景：深拷贝处理的，renderdata和data地址不再等
  _orderData(source, iteratees, orders): any {
    const data = this._order(source, iteratees, orders);
    const cacheData = [...data];
    if (this.traversal) {
      this._traversalOrder(cacheData, iteratees, orders);
      return cacheData;
    }
    return data;
  }

  _pageData(data: T[], page: PageConfig): T[] {
    if (!page || !Object.keys(page).length) {
      return data;
    }
    const startIndex = page.pageIndex * page.pageSize;
    const endIndex = (page.pageIndex + 1) * page.pageSize;
    return data.slice(startIndex, endIndex);
  }

  _sortData(data: T[], sort: SortConfig | null): T[] {
    if (!sort || !Object.keys(sort).length || !sort.direction) {
      return data;
    } else if (sort.customSort) {
      return sort.customSort(data, sort);
    } else {
      return this._orderData(data, [sort.key], [sort.direction]);
    }
  }

  _updatePainator(length: number): void {
    const page = this.paginator;
    // 更新需要在paginator下一个生命周期完成
    Promise.resolve().then((): void => {
      // 长度没有更新不影响page
      if (page.lvTotal === length) {
        return;
      }
      page.lvTotal = length;
      this._internalPageChange$.next();
    });
  }

  _filterStateData(data: T[], filter: Map<string, FilterConfig>): T[] {
    let _res = data || [];
    if (filter && data.length) {
      filter.forEach((item): T[] => (_res = this._filterData(_res, item)));
    }

    // filter 会改变data长度 影响分页
    if (this.paginator) {
      this._updatePainator(_res.length);
    }
    this.filteredData$.next(_res);
    return _res;
  }

  _traversalOrder(source, iteratees, orders): void {
    _forEach(source, (item, key): void => {
      if (!item.children || !item.children.length) {
        return;
      }
      const temp = { ...item };
      temp.children = this._order(item.children, iteratees, orders);
      source[key] = temp;
      this._traversalOrder(source[key].children, iteratees, orders);
    });
  }

  getPureSort(): BehaviorSubject<any> | Observable<null> {
    const { sort } = this;
    const key = 'sortSub';
    const output = new BehaviorSubject<any>(null);
    const source = this._listSubscription.get(key);
    if (!sort) {
      return of(null);
    }
    source?.unsubscribe();
    const sub = sort.lvSortChange.subscribe((res): void => output.next(res));
    this._listSubscription.set(key, sub);
    return output;
  }

  getPurePaginator(): BehaviorSubject<any> | Observable<null> {
    const page = this.paginator;
    const key = 'paginatorSub';
    const output = new BehaviorSubject<any>(null);
    const source = this._listSubscription.get(key);
    if (!page) {
      return of(null);
    }
    source?.unsubscribe();
    const sub = page.lvPageChange.subscribe((res): void => output.next(res));
    this._listSubscription.set(key, sub);
    return output;
  }

  /**
   * 通过[lvPaginaor]='' 方式建立连接
   * pagechange、外部导致变更page流和初始化的合并
   *
   */
  getPaginator(pure: boolean = false): any {
    const page = this.paginator;
    if (!page) {
      return of(null);
    }
    return !pure
      ? merge(page.lvPageChange, this._internalPageChange$).pipe(
          startWith({}),
          delay(0), // pagesize同步修改时，需要page组件中的pagesize得到正确值后，再执行赋值操作
          map((): { total: number; pageIndex: number; pageSize: number } =>
            // page统一api使用，不关心内部变量
            ({
              pageIndex: page.lvPageIndex,
              pageSize: page.lvPageSize,
              total: page.lvTotal,
            }),
          ),
        )
      : page.lvPageChange;
  }

  _filterData(data: T[], filter: FilterConfig | null): T[] {
    const cacheData = this.traversal ? TreeFilterUtils.clone(data) : data;

    const traversalFilter = (): T[] =>
      TreeFilterUtils.filter(cacheData, filter, filter.customItemFilter, this.traversal ? 'children' : '');

    if (!filter || !Object.keys(filter).length) {
      return data;
    } else if (filter.customItemFilter) {
      return traversalFilter();
    } else if (filter.customFilter) {
      return filter.customFilter(data, filter);
    } else if (!FilterUtils[filter.filterMode]) {
      return data;
    } else {
      return traversalFilter();
    }
  }

  /**
   * 先确定该列是否有非空字符串。如果有非空字符串，null undefined NaN处理成空字符串
   * 为了兼容同一列出现字符串、数字的数据时，数字转换成字符串返回给lodash，不会改变源数据的类型
   */
  _order(source, iteratees, orders): any {
    // 当前前端排序仅支持单列排序，多列排序需审查是否影响排序
    const cacheIteratees = iteratees.map((key): { colKey: any; isString: boolean } => {
      const cacheItem = source.find((item): boolean => item[key] !== '' && typeof item[key] === 'string');
      return {
        colKey: key,
        isString: Boolean(cacheItem),
      };
    });
    return _orderBy(
      source,
      cacheIteratees.map((colItem): ((item) => any) => (item): any => {
        const res = item[colItem.colKey];
        if (this._isInvalid(res)) {
          return colItem.isString ? '' : -Infinity;
        } else {
          return colItem.isString ? `${res}` : res;
        }
      }),
      orders,
    );
  }

  _updateStateSubscription(): void {
    const sortChange$ = this.getPureSort() as BehaviorSubject<any>;
    const sortStateChange$ = this.getSortState();
    const filterStateChange$ = this.getFilterState();
    const pageStateChange$ = this.getPaginatorState();
    const pageChange$ = this.getPurePaginator() as BehaviorSubject<any>;

    this._stateChangesSubscription.unsubscribe();
    this._stateChangesSubscription = combineLatest([
      sortChange$,
      sortStateChange$,
      filterStateChange$,
      pageStateChange$,
      pageChange$,
    ])
      .pipe(
        debounceTime(50), // 防止数据抖动（page是异步操作）
        skip(1), // 排除初始化第一次
      )
      .subscribe(([sort, sortState, filterState, paginatorState, paginator]): void => {
        this._state$.next({
          sort,
          sortState,
          filterState,
          paginatorState,
          paginator,
        });
      });
  }

  /**
   * 数据处理流
   * data->[sort direcitve]->-[filter directive]->-[page directive]->-[sort state]->-[filter state]->-[page state]->renderdata
   * NEED_DO: sort 是只支持单列排序，sort sortState配置会同时生效
   * 每次数据流的变更都是由于1. 内部条件变更触发 data->(state)->renderdata 2. data变更
   * 如果是异步，只订阅state变更 state change
   */
  _updateChangeSubscription(): void {
    if (this.async) {
      this._renderChangesSubscription.unsubscribe();
      this._updateAsyncChangeSubscription();
      return;
    }

    // 获取条件的方式和异步一致
    this._updateStateSubscription();

    const sortChange$ = this.getSort();
    const sortStateChange$ = this.getSortState();
    const filterStateChange$ = this.getFilterState();
    const pageStateChange$ = this.getPaginatorState();
    const pageChange$ = this.getPaginator() as Observable<{
      pageIndex: number;
      pageSize: number;
      total: number;
    }>;

    const dataStream = this._data$;

    const sortStream = combineLatest([dataStream, sortChange$]).pipe(
      map((res: any[]): any => this._sortData(res[0], res[1] as SortConfig)),
    );

    const sortStateStream = combineLatest([sortStream, sortStateChange$]).pipe(
      map((res): any[] => this._sortStateData(res[0], res[1])),
    );

    const filterStateStream = combineLatest([sortStateStream, filterStateChange$]).pipe(
      map((res: any): any[] => this._filterStateData(res[0], res[1])),
    );

    /**
     * pageChange: undefined: internal, null: no paginator, object: paginator
     */
    const pageStream = combineLatest([filterStateStream, pageChange$]).pipe(
      map((res): any[] => this._pageData(res[0], res[1] as PageConfig)),
    );

    const pageStateStream = combineLatest([pageStream, pageStateChange$]).pipe(
      map((res: any): any[] => this._pageData(res[0], res[1])),
    );

    this._renderChangesSubscription.unsubscribe();
    this._renderChangesSubscription = pageStateStream
      .pipe(
        debounceTime(50), // 防止数据抖动（page是异步操作）
      )
      .subscribe((data: T[]): void => {
        this._renderData$.next(data);
      });
  }
}
